define(function (require, exports, module) {
    return function (jQuery) {

        jQuery.fn.extend({
            everyTime: function (interval, label, fn, times, belay) {
                return this.each(function () {
                    jQuery.timer.add(this, interval, label, fn, times, belay);
                });
            },
            oneTime: function (interval, label, fn) {
                return this.each(function () {
                    jQuery.timer.add(this, interval, label, fn, 1);
                });
            },
            stopTime: function (label, fn) {
                return this.each(function () {
                    jQuery.timer.remove(this, label, fn);
                });
            }
        });

        jQuery.extend({
            timer: {
                guid: 1,
                global: {},
                regex: /^([0-9]+)\s*(.*s)?$/,
                powers: {
                    // Yeah this is major overkill...
                    'ms': 1,
                    'cs': 10,
                    'ds': 100,
                    's': 1000,
                    'das': 10000,
                    'hs': 100000,
                    'ks': 1000000
                },
                timeParse: function (value) {
                    if (value == undefined || value == null)
                        return null;
                    var result = this.regex.exec(jQuery.trim(value.toString()));
                    if (result[2]) {
                        var num = parseInt(result[1], 10);
                        var mult = this.powers[result[2]] || 1;
                        return num * mult;
                    } else {
                        return value;
                    }
                },
                add: function (element, interval, label, fn, times, belay) {
                    var counter = 0;

                    if (jQuery.isFunction(label)) {
                        if (!times)
                            times = fn;
                        fn = label;
                        label = interval;
                    }

                    interval = jQuery.timer.timeParse(interval);

                    if (typeof interval != 'number' || isNaN(interval) || interval <= 0)
                        return;

                    if (times && times.constructor != Number) {
                        belay = !!times;
                        times = 0;
                    }

                    times = times || 0;
                    belay = belay || false;

                    if (!element.$timers)
                        element.$timers = {};

                    if (!element.$timers[label])
                        element.$timers[label] = {};

                    fn.$timerID = fn.$timerID || this.guid++;

                    var handler = function () {
                        if (belay && this.inProgress)
                            return;
                        this.inProgress = true;
                        if ((++counter > times && times !== 0) || fn.call(element, counter) === false)
                            jQuery.timer.remove(element, label, fn);
                        this.inProgress = false;
                    };

                    handler.$timerID = fn.$timerID;

                    if (!element.$timers[label][fn.$timerID])
                        element.$timers[label][fn.$timerID] = window.setInterval(handler, interval);

                    if (!this.global[label])
                        this.global[label] = [];
                    this.global[label].push(element);

                },
                remove: function (element, label, fn) {
                    var timers = element.$timers, ret;

                    if (timers) {

                        if (!label) {
                            for (label in timers)
                                this.remove(element, label, fn);
                        } else if (timers[label]) {
                            if (fn) {
                                if (fn.$timerID) {
                                    window.clearInterval(timers[label][fn.$timerID]);
                                    delete timers[label][fn.$timerID];
                                }
                            } else {
                                for (var fn in timers[label]) {
                                    window.clearInterval(timers[label][fn]);
                                    delete timers[label][fn];
                                }
                            }

                            for (ret in timers[label]) break;
                            if (!ret) {
                                ret = null;
                                delete timers[label];
                            }
                        }

                        for (ret in timers) break;
                        if (!ret)
                            element.$timers = null;
                    }
                }
            }
        });

        if (jQuery.browser.msie)
            jQuery(window).one("unload", function () {
                var global = jQuery.timer.global;
                for (var label in global) {
                    var els = global[label], i = els.length;
                    while (--i)
                        jQuery.timer.remove(els[i], label);
                }
            });

    }
});